//Jeffery Zhu
//Rui Liu
//CS380c
//Translator from 3-addr to C
#include <iostream>
#include <fstream>
#include <vector>
#include "parser.h"

using namespace std;

class FuncTranslator{
public:
	vector<Instr> & is;
	bool isMain;
	int entry; //entry address
	int end; 
	int nParam; //num of parameters
	int nVar; //num of local variables
	vector<string> statements; //translated statements within the function
	ofstream headerfile;

	FuncTranslator(IR& ir, int e): is(ir.instructions), isMain(false), entry(e), end(0), nParam(0), nVar(0), statements(), headerfile("translated_functions.h"){
		headerfile << "//Contains declarations for translated functions\n\n";
	}


	void output(ostream& o){
		for(int i = 0 ; i < statements.size(); i++){
			o<<statements[i];
		}
	}
	bool translate(){
		statements.clear();
		int i = entry;
		while(i < is.size() && is[i].opt != ENTER){
			if(is[i].opt == ENTRYPC)
				isMain = true;
			i++;
		}

		if(i < is.size()){
			nVar = is[i].param1.value/8;
			entry = i;
		}
		else
			return false;

		while(i < is.size() && is[i].opt != RET){
			i++;
		}

		if(i < is.size()){
			nParam = is[i].param1.value/8;
			end = i;
		}
		else
			return false;

		for(i = entry; i <= end; i++){
			translateStatement(i);
		}
		return true;
	}
//{UNDEF, NOP, ADD, SUB, MUL, DIV, MOD, NEG, CMPEQ, CMPLE, CMPLT, BR, BLBC, BLBS, CALL, LOAD, STORE, MOVE, READ, WRITE, WRL, PARAM, ENTER, RET, ENTRYPC};
	void translateStatement(int idx){
		stringstream ss;
		if(idx >= 1 && idx < is.size()){
			Instr instr = is[idx];
			ss << "reg[" << idx << "]=";
			string reg = ss.str();
			ss.str("");
			ss << "label_" << idx <<":";

			string p1, p2;
			translateParam(instr.param1, p1);
			translateParam(instr.param2, p2);
			switch(instr.opt){
			case ADD:
				ss << reg;
				ss << "(long)(" << p1 << ")+(long)(" << p2 <<");\n";
				break;
			case SUB:
				ss << reg;
				ss << "(long)(" << p1 << ")-(long)(" << p2 <<");\n";
				break;
			case MUL:
				ss << reg;
				ss << "(long)(" << p1 << ")*(long)(" << p2 <<");\n";
				break;
			case DIV:
				ss << reg;
				ss << "(long)(" << p1 << ")/(long)(" << p2 <<");\n";
				break;
			case MOD:
				ss << reg;
				ss << "(long)(" << p1 << ")%(long)(" << p2 <<");\n";
				break;
			case NEG:
				ss << reg;
				ss << "-(long)(" << p1<<");\n";
				break;
			case CMPEQ:
				ss << reg;
				ss << "(long)(" << p1 << ")==(long)(" << p2 <<");\n";
				break;
			case CMPLE:
				ss << reg;
				ss << "(long)(" << p1 << ")<=(long)(" << p2 <<");\n";
				break;
			case CMPLT:
				ss << reg;
				ss << "(long)(" << p1 << ")<(long)(" << p2 <<");\n";
				break;
			case BR:
				ss << "goto " << p1<<";\n";
				break;
			case BLBC:
				ss << "if((" << p1 << ")==0) goto " << p2<<";\n";
				break;
			case BLBS:
				ss << "if((" << p1 << ")!=0) goto " << p2<<";\n";
				break;
			case CALL:
				ss << "_push(0);\n";//LNK?
				ss << "_push(_fp);\n";//save previous fp
				ss << "function_" << instr.param1.value << "();\n";
				break;
			case LOAD:
				ss << reg;
				ss << "*((long *)(" << p1 << "));\n";
				break;
			case STORE:
				ss << "*((long *)(" << p2 << ")) = (long)(" << p1<<");\n";
				break;
			case MOVE:
				ss << p2 << "=(long)(" << p1 <<");\n";
				ss << reg;
				ss << "(long)(" << p2 << ");\n";
				break;
			case READ:
				ss << "ReadLong(reg[" << idx << "]);\n";
				break;
			case WRITE:
				ss << "WriteLong((long)(" << p1 << "));\n";
				break;
			case WRL:
				ss << "WriteLine();\n";
				break;
			case PARAM:
				ss << "_push((long)(" << p1 << "));\n";
				break;
			case ENTER:
				ss.str("");
				if(isMain)
					ss << "void main(){\n";
				else{
					ss << "void function_" << idx << "(){\n";
					headerfile << "void function_" << idx << "();\n\n";
				}
				ss << "_fp = _sp;\n";
				ss << "_alloc(" << nVar << ");\n";
				break;
			case RET:
				ss << "_fp = (long *)*(_fp);\n";
				ss << "_pop(" << (nVar + nParam + 2) << ");\n";
				ss << "return;\n}\n";
				break;
			default: return;
			}
			statements.push_back(ss.str());
		}
	}
//{NA, GP, FP, CONST, ADDR_OFFSET, FIELD_OFFSET, LOCAL_VAR, REG, LABEL};
	void translateParam(Param& p, string& cr){
		stringstream ss;
		switch(p.type){
			case NA: cr = "";break;
			case GP: cr = "_gp";break;
			case FP: cr = "_fp";break;
			case CONST: ss << p.value; cr = ss.str();break;
			case ADDR_OFFSET: ss << (p.value); cr = ss.str();break;
			case FIELD_OFFSET: ss << (p.value); cr = ss.str();break;
			case LOCAL_VAR: ss << "(*(_fp + " << (p.value/8) << "))"; cr = ss.str(); break;
			case REG: ss << "reg[" << p.value << "]"; cr = ss.str();break;
			case LABEL: ss << "label_" << p.value; cr = ss.str();break;
		}
	}
};


int main(int argc, char * argv[]){
	
	IR ir(cin);
	
	ostream &o = cout;
	o << "#include <stdio.h>"<<endl;
	o << "#define WriteLine() printf(\"\\n\");" <<endl;
	o << "#define WriteLong(x) printf(\" %lld\", (long)x);" <<endl;
	o << "#define ReadLong(a) if (fscanf(stdin, \"%lld\", &a) != 1) a = 0;" <<endl;
	o << "#define long long long" << endl;
	o << "#define _push(a) _sp = _sp - 1; *(_sp) = (a);" << endl;
	o << "#define _pop(a) _sp = _sp + (a);" << endl;
	o << "#define _alloc(a) _sp = _sp - (a);" << endl;

	o << "long _global_var[32768/8];"<<endl;
	o << "long * _gp = _global_var;" << endl;
	o << "long reg[" << ir.instructions.size() << "];" <<endl;
	o << "long stack[4096];" << endl;
	o << "long * _sp = stack + 4096;" << endl;
	o << "long * _fp = 0;" << endl<<endl;

	FuncTranslator ft(ir, 1);
	while(ft.entry < ir.instructions.size() && ft.translate()){
		ft.output(o);
		ft.entry = ft.end + 1;
	}

	ft.headerfile.close();
}
